package com.lw.smartquiz.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lw.smartquiz.common.BaseResponse;
import com.lw.smartquiz.common.ErrorCode;
import com.lw.smartquiz.common.ResultUtils;
import com.lw.smartquiz.constant.AIConstant;
import com.lw.smartquiz.constant.CommonConstant;
import com.lw.smartquiz.exception.ThrowUtils;
import com.lw.smartquiz.manager.AIManager;
import com.lw.smartquiz.mapper.QuestionMapper;
import com.lw.smartquiz.model.dto.question.AIGenerateQuestionRequest;
import com.lw.smartquiz.model.dto.question.QuestionContentDTO;
import com.lw.smartquiz.model.dto.question.QuestionQueryRequest;
import com.lw.smartquiz.model.entity.App;
import com.lw.smartquiz.model.entity.Question;
import com.lw.smartquiz.model.entity.User;
import com.lw.smartquiz.model.enums.AppTypeEnum;
import com.lw.smartquiz.model.vo.QuestionVO;
import com.lw.smartquiz.model.vo.UserVO;
import com.lw.smartquiz.service.AppService;
import com.lw.smartquiz.service.QuestionService;
import com.lw.smartquiz.service.UserService;
import com.lw.smartquiz.utils.SqlUtils;
import com.zhipu.oapi.service.v4.model.ModelData;
import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 题目服务实现
 */
@Service
@Slf4j
public class QuestionServiceImpl extends ServiceImpl<QuestionMapper, Question> implements QuestionService {
    @Resource
    private UserService userService;

    @Resource
    private AppService appService;

    @Resource
    private AIManager aiManager;

    /**
     * 校验数据
     *
     * @param question
     * @param add      对创建的数据进行校验
     */
    @Override
    public void validQuestion(Question question, boolean add) {
        ThrowUtils.throwIf(question == null, ErrorCode.PARAMS_ERROR);
        // 从对象中取值
        String questionContent = question.getQuestionContent();
        Long appId = question.getAppId();

        // 创建数据时，参数不能为空
        if (add) {
            // 补充校验规则
            ThrowUtils.throwIf(StringUtils.isBlank(questionContent), ErrorCode.PARAMS_ERROR, "题目内容不能为空");
        }
        // 修改数据时，有参数则校验
        // 补充校验规则
        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用id非法");
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.PARAMS_ERROR, "应用不存在");
    }

    /**
     * 获取查询条件
     *
     * @param questionQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<Question> getQueryWrapper(QuestionQueryRequest questionQueryRequest) {
        QueryWrapper<Question> queryWrapper = new QueryWrapper<>();
        if (questionQueryRequest == null) {
            return queryWrapper;
        }
        // 从对象中取值
        Long id = questionQueryRequest.getId();
        List<QuestionContentDTO> questionList = questionQueryRequest.getQuestionContent();
        String questionContent = JSONUtil.toJsonStr(questionList);
        Long appId = questionQueryRequest.getAppId();
        Long userId = questionQueryRequest.getUserId();
        Long notId = questionQueryRequest.getNotId();
        String sortField = questionQueryRequest.getSortField();
        String sortOrder = questionQueryRequest.getSortOrder();

        // 补充需要的查询条件
        // 模糊查询
        queryWrapper.like(StringUtils.isNotBlank(questionContent), "questionContent", questionContent);
        // 精确查询
        queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(appId), "appId", appId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取题目封装
     *
     * @param question
     * @param request
     * @return
     */
    @Override
    public QuestionVO getQuestionVO(Question question, HttpServletRequest request) {
        // 对象转封装类
        QuestionVO questionVO = QuestionVO.objToVo(question);

        // 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Long userId = question.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        questionVO.setUser(userVO);
        // endregion

        return questionVO;
    }

    /**
     * 分页获取题目封装
     *
     * @param questionPage
     * @param request
     * @return
     */
    @Override
    public Page<QuestionVO> getQuestionVOPage(Page<Question> questionPage, HttpServletRequest request) {
        List<Question> questionList = questionPage.getRecords();
        Page<QuestionVO> questionVOPage = new Page<>(questionPage.getCurrent(), questionPage.getSize(), questionPage.getTotal());
        if (CollUtil.isEmpty(questionList)) {
            return questionVOPage;
        }
        // 对象列表 => 封装对象列表
        List<QuestionVO> questionVOList = questionList.stream().map(QuestionVO::objToVo).collect(Collectors.toList());

        // 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Set<Long> userIdSet = questionList.stream().map(Question::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));
        // 填充信息
        questionVOList.forEach(questionVO -> {
            Long userId = questionVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            questionVO.setUser(userService.getUserVO(user));
        });
        // endregion

        questionVOPage.setRecords(questionVOList);
        return questionVOPage;
    }

    @Override
    public void validAIGenerateRequest(AIGenerateQuestionRequest aiGenerateQuestionRequest) {
        // 数据校验
        Long appId = aiGenerateQuestionRequest.getAppId();
        Integer questionNumber = aiGenerateQuestionRequest.getQuestionNumber();
        Integer optionNumber = aiGenerateQuestionRequest.getOptionNumber();

        ThrowUtils.throwIf(appId == null || appId <= 0, ErrorCode.PARAMS_ERROR, "应用id非法");
        App app = appService.getById(appId);
        ThrowUtils.throwIf(app == null, ErrorCode.PARAMS_ERROR, "应用不存在");
        ThrowUtils.throwIf(questionNumber == null || questionNumber <= 0, ErrorCode.PARAMS_ERROR, "题目数量非法");
        ThrowUtils.throwIf(optionNumber == null || optionNumber <= 1, ErrorCode.PARAMS_ERROR, "选项数量非法");
    }

    /**
     * 组装发给AI的用户消息，格式为
     * "应用名称，\n" +
     * "【【【应用描述】】】，\n" +
     * "应用类别，\n" +
     * "要生成的题目数，\n" +
     * "每个题目的选项数" +
     *
     * @param request
     * @return
     */
    private String makeAIGenerateQuestionUserMessage(AIGenerateQuestionRequest request, App app){
        StringBuilder userMessage = new StringBuilder();
        userMessage.append(app.getAppName()).append("\n");
        userMessage.append("【【").append(app.getAppDesc()).append("】】").append("\n");
        userMessage.append(AppTypeEnum.getEnumByValue(app.getAppType()).getText()).append("\n");
        userMessage.append(request.getQuestionNumber()).append("\n");
        userMessage.append(request.getOptionNumber());
        return userMessage.toString();
    }

    @Override
    public BaseResponse<List<QuestionContentDTO>> syncGenerateQuestionByAI(AIGenerateQuestionRequest aiGenerateQuestionRequest) {
        ThrowUtils.throwIf(aiGenerateQuestionRequest == null, ErrorCode.PARAMS_ERROR);
        //校验参数
        this.validAIGenerateRequest(aiGenerateQuestionRequest);
        //获取app
        Long appId = aiGenerateQuestionRequest.getAppId();
        App app = appService.getById(appId);
        //组装用户消息
        String userMessage = this.makeAIGenerateQuestionUserMessage(aiGenerateQuestionRequest, app);
        // 调用AI生成题目(同步，非流式，采样温度高)
        String aiResult = aiManager.syncStableRequest(AIConstant.GENERATE_QUESTION_SYSTEM_PROMPT, userMessage);
        // 对AI返回结果进行处理
        int start = aiResult.indexOf('[');
        int end = aiResult.lastIndexOf(']');
        String questionsJson = aiResult.substring(start, end + 1);
        List<QuestionContentDTO> questionList = JSONUtil.toList(questionsJson, QuestionContentDTO.class);
        return ResultUtils.success(questionList);
    }

    @Override
    public SseEmitter streamGenerateQuestionByAI(AIGenerateQuestionRequest aiGenerateQuestionRequest) {
        ThrowUtils.throwIf(aiGenerateQuestionRequest == null, ErrorCode.PARAMS_ERROR);
        //校验参数
        this.validAIGenerateRequest(aiGenerateQuestionRequest);
        //获取app
        Long appId = aiGenerateQuestionRequest.getAppId();
        App app = appService.getById(appId);
        //组装用户消息
        String userMessage = this.makeAIGenerateQuestionUserMessage(aiGenerateQuestionRequest, app);
        Flowable<ModelData> modelDataFlowable = aiManager.streamRequest(AIConstant.GENERATE_QUESTION_SYSTEM_PROMPT, userMessage, null);//采样温度为null则使用默认值0.95
        //创建SSE发射器，0表示永不超时
        SseEmitter emitter=new SseEmitter(0L);
        //大括号计数器
        AtomicInteger count= new AtomicInteger(0);
        StringBuffer contentBuffer = new StringBuffer();

        //流式处理
        modelDataFlowable
                //定义处理的线程池
                .observeOn(Schedulers.io())
                //取出内容字符串
                .map(data -> data.getChoices().get(0).getDelta().getContent())
                //去掉空白字符
                .map(message -> message.replaceAll("\\s", ""))
                //有可能此次字符串全是空白字符，所以过滤字符串，true则通过
                .filter(StrUtil::isNotBlank)
                //字符串转成一个个字符
                .flatMap(message -> {
                    List<Character> characters=new ArrayList<>();
                    for (char c : message.toCharArray()) {
                        characters.add(c);
                    }
                    return Flowable.fromIterable(characters);
                })
                .doOnNext(c -> {
                    //若是左括号，计数器加一
                    if(c.equals('{')){
                        count.incrementAndGet();
                    }
                    //若计数器大于0，拼接字符串
                    if(count.get()>0){
                        contentBuffer.append(c);
                    }
                    //若是右括号，计数器减一
                    if(c.equals('}')){
                        count.decrementAndGet();
                        //若计数器为0，则发送字符串，清空contentBuffer
                        if(count.get()==0){
                            emitter.send(JSONUtil.toJsonStr(contentBuffer.toString()));
                            contentBuffer.setLength(0);
                        }
                    }
                })
                .doOnError(e -> log.error("流式生成题目出错",e))
                .doOnComplete(emitter::complete)
                .subscribe();
        return emitter;
    }

}
